﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using BoldBrick.Core.Logging;
using BoldBrick.SharePoint.CommonControls.DataObjects.Base;
using Microsoft.SharePoint;
using System.Linq;
using Microsoft.SharePoint.Workflow;

namespace BoldBrick.SharePoint.CommonControls.Extensions
{
	/// <summary>
	/// Extension class for <see cref="SPList"/> objects
	/// </summary>
	public static class SPListEx
	{
		private static readonly ILogger log = Logger.GetLogger(typeof (SPListEx));

		#region ------ Event Receivers ------------------------------------------------------------

		/// <summary>
		/// Adds event receiver to list
		/// </summary>
		/// <param name="list">The list</param>
		/// <param name="eventReceiverObject">Event receiver metadata object</param>
		public static void AddEventReceiver(this SPList list, BaseEventReceiverObject eventReceiverObject)
		{
			log.Write(LogLevel.Info, "Adding '{0}' event receiver to '{1}' list.", eventReceiverObject, list.RootFolder.ServerRelativeUrl);

			try
			{
				SPEventReceiverDefinition receiverDefinition = list.EventReceivers.Add();

				receiverDefinition.Assembly = eventReceiverObject.AssemblyFullName;
				receiverDefinition.Type = eventReceiverObject.EventReceiverType;
				receiverDefinition.Class = eventReceiverObject.EventReceiverClassFullName;
				receiverDefinition.SequenceNumber = eventReceiverObject.SequenceNumber;

				receiverDefinition.Update();
				log.Write(LogLevel.Info, "'{0}' event receiver has been added to '{1}' list.", eventReceiverObject, list.RootFolder.ServerRelativeUrl);
			}
			catch (Exception ex)
			{
				log.Write(LogLevel.Error, ex, "Unable to add '{0}' event receiver to '{1}' list.", eventReceiverObject, list.RootFolder.ServerRelativeUrl);
			}

		}

		/// <summary>
		/// Removes event receiver from list
		/// </summary>
		/// <param name="list">The list</param>
		/// <param name="eventReceiverObject">Event receiver metadata object</param>
		public static void RemoveEventReceiver(this SPList list, BaseEventReceiverObject eventReceiverObject)
		{
			log.Write(LogLevel.Info, "Removing '{0}' event receivers from '{1}' list.", eventReceiverObject, list.RootFolder.ServerRelativeUrl);

			try
			{
				List<SPEventReceiverDefinition> eventReceiverDefinitionsToDelete = list.EventReceivers
					.OfType<SPEventReceiverDefinition>()
					.Where(eventReceiverDefinition => eventReceiverObject.IsEqualToDefinition(eventReceiverDefinition))
					.ToList();

				foreach (SPEventReceiverDefinition eventReceiverDefinition in eventReceiverDefinitionsToDelete)
				{
					eventReceiverDefinition.Delete();
				}
				log.Write(LogLevel.Info, "'{0}' event receivers has been removed from '{1}' list.", eventReceiverObject, list.RootFolder.ServerRelativeUrl);
			}
			catch (Exception ex)
			{
				log.Write(LogLevel.Error, ex, "Unable to remove '{0}' event receivers from '{1}' list.", eventReceiverObject, list.RootFolder.ServerRelativeUrl);
			}
		}

		#endregion

		#region ------ Workflows ------------------------------------------------------------------

		/// <summary>
		/// Associates workflow with the list
		/// </summary>
		/// <param name="list">The list</param>
		/// <param name="web">parent web of the list</param>
		/// <param name="workflow">Workflow to association</param>
		public static void AssociateWorkflow(this SPList list, SPWeb web, Workflow workflow)
		{
			SPWorkflowTemplate workflowTemplate = web.WorkflowTemplates[workflow.ID];
			if (workflowTemplate == null)
			{
				string message = string.Format("'{0}' workflow template not found.", workflow.ID);
				throw new Exception(message);
			}


			SPList spListWorkflowHistory = web.GetList(workflow.ListWorkflowHistory);
			if (spListWorkflowHistory == null)
				spListWorkflowHistory = workflow.ListWorkflowHistory.CreateList(web);

			SPList spListWorkflowTasks = web.GetList(workflow.ListWorkflowTasks);
			if (spListWorkflowTasks == null)
				spListWorkflowTasks = workflow.ListWorkflowTasks.CreateList(web);

			string workflowName = workflow.GetName(web.Language);

			web.AllowUnsafeUpdates = true;
			SPWorkflowAssociation workflowAssociation;

			// create content type association
			if (workflow.ContentTypeID.HasValue)
			{
				workflowAssociation = SPWorkflowAssociation.CreateListContentTypeAssociation(
					workflowTemplate,
					workflowName,
					spListWorkflowTasks,
					spListWorkflowHistory);
			}
			// create list association
			else
			{
				workflowAssociation = SPWorkflowAssociation.CreateListAssociation(
					workflowTemplate,
					workflowName,
					spListWorkflowTasks,
					spListWorkflowHistory);
			}

			// Set workflow parameters 
			workflowAssociation.AllowManual = workflow.AllowManual;
			workflowAssociation.AutoStartCreate = workflow.AutoStartCreate;
			workflowAssociation.AutoStartChange = workflow.AutoStartChange;

			if (workflow.ContentTypeID.HasValue)
			{
				SPContentType contentType = list.ContentTypes.GetChildContentType(workflow.ContentTypeID.Value);
				if (contentType == null)
				{
					log.Write(LogLevel.Error, "Child content type for '{0}' ID not found in '{1}' list.", workflow.ContentTypeID, list.RootFolder.ServerRelativeUrl);
					return;
				}
				SPWorkflowAssociation associationCurrent = contentType.WorkflowAssociations.GetAssociationByName(workflowName, web.Locale);
				if (associationCurrent != null)
				{
					log.Write(LogLevel.Error, "Workflow with '{0}' name is already associated with '{1}' content type in '{2}' list.", 
						workflowName, contentType.Name, list.RootFolder.ServerRelativeUrl);
					return;
				}

				contentType.WorkflowAssociations.Add(workflowAssociation);
			}
			else
			{
				// Add workflow association to my list
				list.WorkflowAssociations.Add(workflowAssociation);
			}

			// Enable workflow
			workflowAssociation.Enabled = true;
		}


		#endregion

		#region ------ List Items -----------------------------------------------------------------

		/// <summary>
		/// Gets list item item url
		/// </summary>
		/// <param name="list">Parent list of the item</param>
		/// <param name="pageType">Type of form to show (edit, display, etc.)</param>
		/// <param name="itemID">ID of the item</param>
		/// <returns>Returns URL of the item</returns>
		public static string GetListItemUrl(this SPList list, PAGETYPE pageType, int itemID)
		{
			SPForm form = list.Forms[pageType];
			if (form == null)
			{
				string errorMessage = string.Format("'{0}' form type does not exist for item.", pageType);
				throw new ArgumentException(errorMessage);
			}
			
			return String.Format("{0}?ID={1}", form.ServerRelativeUrl, itemID);
		}

		/// <summary>
		/// Try to get item by ID, returns null if item not found
		/// </summary>
		/// <param name="list">List of the item</param>
		/// <param name="itemID">ID of the item</param>
		/// <returns>Returns item by ID, returns null if item not found</returns>
		public static SPListItem TryGetItemById(this SPList list, int itemID)
		{
			try
			{
				SPListItem item = list.GetItemById(itemID);
				return item;
			}
			catch(Exception ex)
			{
				log.Write(LogLevel.Warn, ex, "'GetItemById' method failed.");
			}

			return null;
		}

		

		#endregion

	}
}
